package com.jpa.util;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Date;
import java.sql.*;
import java.util.*;


/**
 * <p>
 * Title:数据库操作工具封装类
 * </p>
 * <p/>
 * <p/>
 * Description: <p/>
 * <p/>
 * <pre>
 * 对commons DButil DBCP进行封装，便于操作的数据库封装类
 *
 *
 * 使用时应注意:
 * 1、单数据库连接情况下，先要使用单参数init初始化，之后使用无参构造DBBase()进行调用对象的创建。
 *    支持连接池，线程安全！
 * 2、如果需要连接2个以上数据库，需要使用双参数init进行初始化。使用带参构造
 *    DBBase(String cnPoolNativeName)进行调用对象的创建！
 * 3、当然也可以即使用无参构造来调用默认初始化的连接，同时使用带参构造来调用其他别名的数据库连接池。
 *    实际上无参构造相当于调用DBBase(&quot;default&quot;);
 * 4、也可以使用它对同一数据库，进行多次不同昵称的连接池初始化！连接池昵称在该工具类中是唯一标识一个连接池的key，
 *    即使2个不同的昵称他们的Properties都相同，也会被视为不同的连接池。
 * 5、不建议对同一数据库进行多次不同昵称的连接池初始化，除非你认为一定有必要！
 *
 *
 * 注意：
 * 1、不论你以何种方式初始化本数据库操作类，静态init方法调用的初始化只需进行一次。
 * 		重复初始化会得到错误提示，但并不影响工具类的正常运行！
 *
 *
 * 2、数据库中必须存在表ems并且最少有一条记录。或者在初始化时提供&quot;validationQuery&quot;属性，
 * 		提供类似 &quot;select count(1) from ems&quot;这样能查询出最少一行记录的sql语句。
 *      用于连接空闲状态时的检查！由于该查询会被频繁调用，因此不建议sql过于复杂或表中记录数过多！
 * </pre>
 * <p/>
 * <p/>
 * <p>
 * Copyright: Copyright (c) 2011
 * </p>
 * <p/>
 * <p>
 * Company:
 * </p>
 *
 * @author 徐宾
 * @version 1.0
 */
public class DBBase {
    private QueryRunner run;

    private static Hashtable<String, DataSource> dataSourceHash = new Hashtable<String, DataSource>();
    private final static Logger log = LoggerFactory.getLogger(DBBase.class);

    /**
     * 返回默认名称的DataSource连接池对象，默认连接池昵称default
     */
    public DataSource getDataSource() {
        return dataSourceHash.get("default");
    }

    /**
     * <pre>
     * 返回连接池昵称为cnPoolNativeName的DataSource连接池对象
     * 如果cnPoolNativeName值为&quot;default&quot;，则效果与DataSource getDataSource()相同。
     * </pre>
     *
     * @param cnPoolNativeName
     * @return DataSource
     */
    public static DataSource getDataSource(String cnPoolNativeName) {
        return dataSourceHash.get(cnPoolNativeName);
    }

    /**
     * <pre>
     * 初始化DB工具类，传入参数cnPoolNativeNameArray是连接池昵称的数组，
     * propertiesArray对象是对应昵称的配置信息，每个配置必须包括类似如下属性。
     * 对其它DBCP属性有设置需要的，也可以加入其中.useOldAliasMetadataBehavior=true 启用别名机制，
     * 否则mysql5.X的select字段别名不生效
     *
     * 例如：
     * properties.setProperty(&quot;connectionProperties&quot;,&quot;useUnicode=true;characterEncoding=gbk;useOldAliasMetadataBehavior=true&quot;);
     * properties.setProperty(&quot;driverClassName&quot;, &quot;com.mysql.jdbc.Driver&quot;);
     * properties.setProperty(&quot;url&quot;, &quot;jdbc:mysql://135.224.91.108:3306/xj_bk&quot;);
     * properties.setProperty(&quot;username&quot;, &quot;yanjian&quot;);
     * properties.setProperty(&quot;password&quot;, &quot;xxxxx&quot;);
     * properties.setProperty(&quot;validationQuery&quot;, &quot;select count(1) from GateWayConfig&quot;);
     * </pre>
     */
    public static boolean[] init(String[] cnPoolNativeNameArray,
                                 Properties[] propertiesArray) {
        boolean[] res = new boolean[cnPoolNativeNameArray.length];

        for (int i = 0; i < cnPoolNativeNameArray.length; i++) {
            res[i] = createDataSource(cnPoolNativeNameArray[i],
                    propertiesArray[i]);
        }

        return res;
    }

    /**
     * <pre>
     * 初始化DB工具类，传入参数Properties对象，必须包括如下属性。
     * 对其它DBCP属性有设置需要的，也可以加入其中.
     * 注意：
     * useOldAliasMetadataBehavior=true启用别名机制，否则mysql5.X的select字段别名不生效
     *
     * 例如:
     * properties.setProperty(&quot;connectionProperties&quot;,&quot;useUnicode=true;characterEncoding=gbk;useOldAliasMetadataBehavior=true&quot;);
     * properties.setProperty(&quot;driverClassName&quot;, &quot;com.mysql.jdbc.Driver&quot;);
     * properties.setProperty(&quot;url&quot;, &quot;jdbc:mysql://135.224.91.108:3306/xj_bk&quot;);
     * properties.setProperty(&quot;username&quot;, &quot;yanjian&quot;);
     * properties.setProperty(&quot;password&quot;, &quot;xxxxx&quot;);
     * properties.setProperty(&quot;validationQuery&quot;, &quot;select count(1) from GateWayConfig&quot;);
     * </pre>
     */
    public static boolean init(Properties _properties) {
        return createDataSource("default", _properties);
    }


    /**
     * 创建连接池对象！
     *
     * @param pubDb      数据库公共属性
     * @param key
     * @param properties 数据库私有属性
     * @return
     */
    private static boolean createDataSource(Properties pubDb, String key, Properties properties) {
        DataSource ds = getDataSource(key);
        if (ds == null) {
            Properties dbInfo = (Properties) pubDb.clone();
            dbInfo.putAll(properties);
            try {
                ds = (DruidDataSource) DruidDataSourceFactory
                        .createDataSource(dbInfo);
                log.info("Druid 连接池参数-->" + dbInfo);
                dataSourceHash.put(key, ds);
            } catch (Exception ex) {
                log.error("创建" + key + "连接池失败", ex);
                return false;
            }
        } else {
            log.warn("连接池" + key + "已经进行过初始化！无法再次初始化！");
            return false;
        }
        return true;
    }

    /**
     * 创建连接池对象！
     *
     * @param cnPoolNativeName
     * @param _properties
     * @return
     */
    private static boolean createDataSource(String cnPoolNativeName,
                                            Properties _properties) {
        DataSource ds = getDataSource(cnPoolNativeName);
        Properties properties = new Properties();
        properties.setProperty("defaultAutoCommit", "true");
        properties.setProperty("defaultReadOnly", "false");
        properties.setProperty("defaultTransactionIsolation", "READ_COMMITTED");

        properties.setProperty("initialSize", "1");
        properties.setProperty("maxActive", "10");
        properties.setProperty("maxIdle", "5");
        properties.setProperty("maxWait", "60000"); // 毫秒
        properties.setProperty("minIdle", "1");
        properties.setProperty("removeAbandoned", "true");
        properties.setProperty("removeAbandonedTimeout", "65"); // 秒
        properties.setProperty("validationQuery", "select 'x'");
        properties.setProperty("testWhileIdle", "true");
        properties.setProperty("timeBetweenEvictionRunsMillis", "10000");
        properties.setProperty("minEvictableIdleTimeMillis", "300000");
        properties.setProperty("numTestsPerEvictionRun", "10");
        properties.setProperty("type", "1 ");
        properties.putAll(_properties);

        if (ds == null) {
            try {
                ds = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
                log.info("Druid 连接池参数-->" + properties);
                dataSourceHash.put(cnPoolNativeName, ds);
            } catch (Exception ex) {
                log.error("创建" + cnPoolNativeName + "连接池失败", ex);
                return false;
            }
        } else {
            log.warn("连接池" + cnPoolNativeName + "已经进行过初始化！无法再次初始化！");
            return false;
        }
        return true;
    }

    /**
     * <pre>
     * 创建连接池昵称为cnPoolNativeName操作的对象，对应双参数init方法初始化的数据库连接
     *
     * 注意：如果传入cnPoolNativeName值为default，则传回默认初始化的连接池！
     *       与DBBase()效果相同！
     *
     * </pre>
     */
    public DBBase(String cnPoolNativeName) {
        run = createQueryRunner(cnPoolNativeName);
    }

    /**
     * 获取查询器！
     *
     * @param cnPoolNativeName
     */
    private synchronized QueryRunner createQueryRunner(String cnPoolNativeName) {
        DataSource ds = getDataSource(cnPoolNativeName);
        if (ds == null) {
            String message = "";
            if (cnPoolNativeName.equalsIgnoreCase("default")) {
                message = "名称为" + cnPoolNativeName
                        + "连接池未初始化，请先调用DBBase.init(_properties)或DBBase.init("
                        + cnPoolNativeName + ",_properties)方法初始化！";
            } else {
                message = "名称为" + cnPoolNativeName + "连接池未初始化，请先调用DBBase.init("
                        + cnPoolNativeName + ",_properties)方法初始化！";
            }
            log.error(message);
            return null;
        }
        return new QueryRunner(ds);
    }

    /**
     * <pre>
     * 创建默认连接操作的对象，对应init方法初始化的数据库连接;
     * 连接池昵称 default
     *
     * new之前需先调用init方法进行初始化。任何应用程序使用本数据库工具类，
     * 只需要进行一次init初始化，之后随处使用，随处new DBBase对象使用即可！
     * </pre>
     */
    public DBBase() {
        // if (dataSource == null) {
        // log.error("连接池未初始化，请先调用DBBase.init(Properties _properties)方法"
        // + "进行初始化！");
        // }
        // run = new QueryRunner(dataSource);
        run = createQueryRunner("default");
    }

//    private String replaceSql

    /**
     * 查询第一行数据返回map对象
     *
     * @param sql
     * @param params
     * @return
     */
    public Map<String, String> queryForMap(String sql, Object[] params) {
        Map<String, Object> obj = null;
        Map<String, String> result = null;
        try {
            showSql(sql);
            obj = run.query(sql, new MapHandler(), params);
        } catch (SQLException e) {
            log.error("", e);
        }
        result = getStringStringMap(obj, result);
        return result;
    }

    /**
     * 将Map<String,Object>对象转换为Map<String, String>对象
     *
     * @param obj
     * @param result
     * @return
     */
    private Map<String, String> getStringStringMap(Map<String, Object> obj, Map<String, String> result) {
        if (obj != null) {
            result = new LinkedHashMap<String, String>();
            Object value;
            Iterator iter = obj.keySet().iterator();
            String key;
            while (iter.hasNext()) {
                key = iter.next().toString();
                value = obj.get(key);
                if (value == null)
                    result.put(key, "");
                else if (value instanceof Clob) {
                    Clob clob = (Clob) value;
                    try {
                        result.put(key, clob.getSubString(1, (int) clob.length()));
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                } else if (value instanceof Timestamp) {
                    Date date = new Date(((Timestamp) value).getTime());
                    String str = TimeUtil.timeToStringAccordingFormat(date, "yyyyMMddHHmmss");
                    result.put(key, str);
                } else {
                    result.put(key, value.toString());
                }
            }
        }
        return result;
    }


    /**
     * <pre>
     * 查询返回List对象，List中是Map对象，每个Map对应一条记录！
     *
     * sql中无?号参数时，params可以为null
     * </pre>
     *
     * @param sql    String
     * @param params Object[]
     * @return List
     */
    @SuppressWarnings("unchecked")
    public List<Map<String, String>> queryForMapStrList(String sql, Object[] params) throws Exception {
        List<Map<String, Object>> obj = null;
        List<Map<String, String>> result;
        try {
            showSql(sql);
            obj = run.query(sql, new MapListHandler(), params);
        } catch (SQLException e) {
            log.error("", e);
            throw new Exception("数据库获取数据出错：",e);
        }
        if (obj == null)
            return null;
        else {
            result = new ArrayList<Map<String, String>>(obj.size());
            for (Map<String, Object> map : obj) {
                Map<String, String> newMap = null;
                newMap = getStringStringMap(map, newMap);
                result.add(newMap);
            }
        }
        return result;
    }

    /**
     * <pre>
     * 查询返回List对象，List中是Map对象，每个Map对应一条记录！
     *
     * sql中无?号参数时，params可以为null
     * </pre>
     *
     * @param sql    String
     * @param params Object[]
     * @return List
     */
    @SuppressWarnings("unchecked")
    public List<Map<String, Object>> queryForMapList(String sql, Object[] params) {
        List<Map<String, Object>> obj = null;
        try {
            showSql(sql);
            obj = run.query(sql, new MapListHandler(), params);
        } catch (SQLException e) {
            log.error("", e);
        }
        return obj;
    }

    /**
     * <pre>
     * 查询返回单个对象，class是符合Java Bean规范的类。
     * 类中属性名与select的字段名称完全相同即可，类型与字段类型相同。
     * </pre>
     *
     * @param sql   String
     * @param clazz Class
     * @return T
     */
    @SuppressWarnings("unchecked")
    public <T> T queryForObject(String sql, Class<T> clazz) {
        T obj = null;
        try {
            showSql(sql);
            obj = (T) run.query(sql, new BeanHandler(clazz));
        } catch (SQLException e) {
            log.error("", e);
        }
        return obj;
    }

    /**
     * <pre>
     * 查询返回单个对象，class是符合Java Bean规范的类。
     * 类中属性名与select的字段名称完全相同即可，类型与字段类型相同。
     *
     * sql中无?号参数时，params可以为null
     * </pre>
     *
     * @param sql   String
     * @param param Object[]
     * @param clazz Class
     * @return T
     */
    @SuppressWarnings("unchecked")
    public <T> T queryForObject(String sql, Object param[], Class<T> clazz) {
        T obj = null;
        try {
            showSql(sql);
            obj = (T) run.query(sql, new BeanHandler(clazz), param);
        } catch (SQLException e) {
            log.error("", e);
        }
        return obj;
    }

    /**
     * <pre>
     * 查询返回List对象，List中是T类型对象，每个Class对象对应一条记录！
     * 类似Hibernate，只是比Hibernate轻量简单！
     * </pre>
     *
     * @param sql   String
     * @param clazz Class
     * @return List<T>
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> queryForObjectList(String sql, Class<T> clazz) {
        List<T> obj = null;
        try {
            showSql(sql);
            obj = (List<T>) run.query(sql, new BeanListHandler(clazz));
        } catch (SQLException e) {
            log.error("", e);
        }
        return obj;
    }

    /**
     * <pre>
     * 查询返回List对象，List中是T类型对象，每个Class对象对应一条记录！
     * 类似Hibernate，只是比Hibernate轻量简单！
     *
     * sql中无?号参数时，params可以为null sql中无?号参数时，params可以为null
     * </pre>
     *
     * @param sql   String
     * @param param Object[]
     * @param clazz Class
     * @return List<T>
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> queryForObjectList(String sql, Object[] param,
                                          Class<T> clazz) {
        List<T> obj = null;
        try {
            showSql(sql);
            obj = (List<T>) run.query(sql, new BeanListHandler(clazz), param);
        } catch (SQLException e) {
            log.error("", e);
        }
        return obj;
    }

    /**
     * <pre>
     * 统计类的查询，返回第一行第一列的数值，作为计数，
     * 一般都是select count 系列语句调用此方法。
     * </pre>
     *
     * @param sql String
     * @return int
     */
    public int queryCount(String sql) {

        Object o = queryR1C1(sql, null);
        if (o instanceof Integer) {
            return (Integer) o;
        }
        if (o instanceof Long) {
            Long l = (Long) o;
            return l.intValue();
        }
        if (o instanceof String) {
            String s = (String) o;
            try {
                return Integer.parseInt(s);
            } catch (NumberFormatException e) {
                return -1;
            }
        }
        return -1;
    }

    /**
     * <pre>
     * 统计类的查询，返回第一行第一列的数值，作为计数，一般都是select count 系列语句调用此方法。
     * sql中无?号参数时，params可以为null sql中无?号参数时，params可以为null
     * </pre>
     *
     * @param sql    String
     * @param params Object[]
     * @return int
     */
    public int queryCount(String sql, Object[] params) {

        Object o = queryR1C1(sql, params);
        if (o instanceof Integer) {
            return (Integer) o;
        }
        if (o instanceof Long) {
            Long l = (Long) o;
            return l.intValue();
        }
        if (o instanceof String) {
            String s = (String) o;
            try {
                return Integer.parseInt(s);
            } catch (NumberFormatException e) {
                return -1;
            }
        }
        return -1;
    }

    /**
     * <pre>
     * 查询返回结果集的第一行第一列的对象。
     * 返回对象类型由字段类型决定 sql中无?号参数时，params可以为null
     * </pre>
     *
     * @param sql String
     * @return java.lang.Object
     */
    public Object queryR1C1(String sql) {

        showSql(sql);
        Object s = null;
        try {
            s = run.query(sql, new ScalarHandler(1));
        } catch (SQLException e) {
            log.error("", e);
        }
        return s;
    }

    /**
     * <pre>
     * 查询返回结果集的第一行第一列的对象。
     * 返回对象类型由字段类型决定 sql中无?号参数时，params可以为null
     * </pre>
     *
     * @param sql    String
     * @param params Object[]
     * @return java.lang.Object
     */
    public Object queryR1C1(String sql, Object[] params) {

        showSql(sql);
        Object s = null;
        try {
            s = run.query(sql, new ScalarHandler(1), params);
        } catch (SQLException e) {
            log.error("", e);
        }
        return s;
    }

    /**
     * <pre>
     * 对存储过程执行的封装。
     * 专用于执行Mysql的存储过程！mysql5.1后续版本支持存储过程！
     *
     * 返回List对象容器，List容器中第0个元素是存储过程执行结果，
     * 如果返回结果为Null，则是执行中出现了异常！
     * 对于多个返回值的存储过程，List容器中从第1个元素开始，
     * 按照outParameSqlTypes指定类型对应的Java对象类型依次放入List容器，一同返回。
     *
     * 注意：参数中outParameSqlTypes参数是java.sql.Types枚举对象的对象类型值数组。
     *      调用时,sql语句强制要求,in类型参数顺序的放在最前,out类型参数顺序的放在最后！
     *      允许除sql外的其它参数为null，即如果需要调用无参存储过程，则直接给sql，和两个null即可。
     *
     * 调用示例,例如:
     * 存储过程定义为：
     * testProc(IN param1 BIGINT,IN param2 BIGINT,OUT param3 VARCHAR(255))
     *
     * 调用本方法时sql参数写为：
     * &quot;{call testProc(?,?,?)}&quot;
     *
     * 调用本方法时inParameters参数为：
     * new Object[]{new Long(2323),new Long(56565)};
     *
     * 调用本方法时outParameSqlTypes参数为:
     * new int[]{java.sql.Types.VARCHAR };
     *
     * 调用正常List返回对象中
     * 第0个元素为 Interger类型的存储过程执行结果值。
     * 第1个元素为 String类型的outParame返回值。
     *
     *
     * </pre>
     *
     * @param sql
     * @param inParameters
     * @param outParameSqlTypes
     * @return List<Object>
     */
    public List<Object> executeMysqlStoredProcedure(String sql,
                                                    Object[] inParameters, int[] outParameSqlTypes) {
        showSql(sql);
        // 防止空指针引发无法调用无参存储过程
        if (inParameters == null) {
            inParameters = new Object[0];
        }
        if (outParameSqlTypes == null) {
            outParameSqlTypes = new int[0];
        }

        ArrayList<Object> ls = new ArrayList<Object>();
        Connection con = null;
        CallableStatement cstmt = null;

        try {
            con = run.getDataSource().getConnection();
            cstmt = con.prepareCall(sql);
            for (int i = 0; i < inParameters.length; i++) {
                cstmt.setObject(i + 1, inParameters[i]);
            }
            for (int j = 0; j < outParameSqlTypes.length; j++) {
                cstmt.registerOutParameter(j + 1 + inParameters.length,
                        outParameSqlTypes[j]);
            }
            int res = cstmt.executeUpdate();
            ls.add(new Integer(res));
            for (int j = 0; j < outParameSqlTypes.length; j++) {
                ls.add(cstmt.getObject(j + 1 + inParameters.length));
            }
            return ls;
        } catch (SQLException ex) {
            log.error("executeStoredProcedure  failed. ", ex);
            return null;
        } finally {
            try {
                DbUtils.close(cstmt);
            } catch (Exception ex) {

            }
            try {
                DbUtils.close(con);
            } catch (Exception ex) {

            }
        }
    }

    /**
     * 执行更新、删除、插入,语句。
     *
     * @param sql String
     * @return int
     */
    public int executeSql(String sql) {
        int i = 0;
        try {
            showSql(sql);
            i = run.update(sql);
        } catch (SQLException e) {
            log.error("", e);
        }
        return i;
    }

    /**
     * 执行更新、删除、插入。 sql中无?号参数时，params可以为null
     *
     * @param sql   String
     * @param param Object[]
     * @return int
     */
    public int executeSql(String sql, Object[] param) throws SQLException {
        int i = 0;
//        try {
//            showSql(sql);
            i = run.update(sql, param);
//        } catch (SQLException e) {
//            i=-1;
//            log.error("", e);
//        }
        return i;
    }

    /**
     * 执行更新、删除、插入。 sql中无?号参数时，params可以为null
     *
     * @param sql   String
     * @param param Object[]
     * @return int
     */
    public int executeSql(Connection connection,String sql, Object[] param) {
        int i = 0;
        try {
            //showSql(sql);
            i = run.update(connection,sql, param);
        } catch (SQLException e) {
            log.error("", e);
            return -1;
        }
        return i;
    }

    /**
     * 执行批量更新、删除、添加，参数是sql的?号数组。 sql不带?号，param可以为null
     *
     * @param sql   String
     * @param param Object[][]
     * @return int[]
     */
    public int[] executeBeachSql(String sql, Object[][] param) {
        int res[] = null;

        try {
            showSql(sql);
            res = run.batch(sql, param);
        } catch (SQLException e) {
            log.error("", e);
        }
        return res;
    }

    /**
     * 执行批量更新、删除、添加，参数是sql的?号数组。 sql不带?号，param可以为null
     *
     * @param sql   String
     * @param param Object[][]
     * @return int[]
     */
    public int[] executeBeachSql(Connection connection,String sql, Object[][] param) throws SQLException {
        int res[] = null;

        //try {
            //showSql(sql);
            res = run.batch(connection,sql, param);
        //} catch (SQLException e) {
        //    log.error("", e);
        //}
        return res;
    }

    /**
     * 执行查询，如果有反馈结果就返回true，如果结果集为空返回false
     *
     * @param sql String
     * @return boolean
     */
    public boolean isExists(String sql) {
        if (queryR1C1(sql) == null) {
            return false;
        } else {
            return true;
        }

    }

    /**
     * 向tablename表中，插入一条记录，信息由Map对象提供。
     *
     * @param tableName String
     * @param data      Map
     * @return int
     */
    @SuppressWarnings("unchecked")
    public int insert(String tableName, Map data) {

        int i = -1;
        try {
            i = run.update(getBeachInsertSql(tableName, data), data.values()
                    .toArray());
            return i;
        } catch (SQLException ex) {
            log.error("", ex);
        }
        return i;
    }

    /**
     * <pre>
     * 向tablename表中，插入一条记录，信息由Object对象提供。
     * 另外，obj对象参数类型符合标准JavaBean规范的字段才会被存储！
     * 也即属性字段如果没有对应的public的标准get、set方法，对应属性就无法被存储！
     * </pre>
     *
     * @param tableName String
     * @param obj       Object
     * @return int
     */
    public int insert(String tableName, Object obj) {
        Object[] param = getParam(obj);
        int i = -1;
        try {
            i = run.update(getBeachInsertSql(tableName, obj), param);
            return i;
        } catch (SQLException ex) {
            log.error("", ex);
        }
        return i;
    }

    /**
     * <pre>
     * 向与对象类名同名的表中，插入一条记录，信息由Object对象提供。
     * 数据库中必须存在与对象类名同名的表
     * 另外，obj对象参数类型符合标准JavaBean规范的字段才会被存储！
     * 也即属性字段如果没有对应的public的标准get、set方法，对应属性就无法被存储！
     *
     * </pre>
     *
     * @param obj Object
     * @return int
     */
    public int insert(Object obj) {
        String tableName = obj.getClass().getSimpleName();
        return insert(tableName, obj);
    }

    /**
     * <pre>
     * 使用批量策略向数据库执行sql语句。
     * sqls参数是sql语句形成的String List链。
     * 返回结果的int数组，对应着每条记录的执行结果。
     *
     * 注意：传入的参数有多少条记录，多少条记录就会成为一批数据进行一次执行。
     *       部分数据库缓冲较小，数量单位请自行控制！
     * </pre>
     *
     * @param sqlList List<String>
     * @return int[]
     */
    public int[] executeBeachSql(List<String> sqlList) {

        if (sqlList == null || sqlList.size() == 0) {
            log.warn("No any data for insert!");
            return null;
        }

        Connection con = null;
        try {
            con = run.getDataSource().getConnection();
            return executeBeachSqls(
                    sqlList.toArray(new String[sqlList.size()]), con);
        } catch (SQLException ex) {
            log.error("BeachInsert data  failed. ", ex);
            return null;
        } finally {
            try {
                DbUtils.close(con);
            } catch (Exception ex) {

            }
        }
    }

    /**
     * <pre>
     * 使用批量策略向数据库执行sql语句。
     * sqls参数是sql语句形成的String List链。
     * 返回结果的int数组，对应着每条记录的执行结果。
     *
     * 注意：不管传入多少条记录，单批次执行的最大记录数是beachCount,
     *      自动按照beachCount分批插入
     * </pre>
     *
     * @param sqlList    List<String>
     * @param beachCount int
     */
    public void executeBeachSql(List<String> sqlList, int beachCount) {
        if (sqlList == null || sqlList.size() == 0) {
            log.warn("No any data for insert!");
            return;
        }

        executeBeachSql(sqlList.toArray(new String[sqlList.size()]), beachCount);
    }

    /**
     * <pre>
     * 使用批量策略向数据库执行sql语句。sqls参数是sql语句形成的String数组。
     *
     * 注意：不管传入多少条记录，单批次执行的最大记录数是beachCount,
     *       自动按照beachCount分批插入
     * </pre>
     *
     * @param sqls       String[]
     * @param beachCount int
     */
    public void executeBeachSql(String[] sqls, int beachCount) {

        if (sqls == null || sqls.length == 0) {
            log.warn("No any data for insert!");
            return;
        }

        Connection con = null;
        try {
            con = run.getDataSource().getConnection();
            int[] rows;
            String[] sqls_temp = new String[beachCount];
            int pageCursor = 0;
            for (int i = 0; i < sqls.length; i++) {
                pageCursor = i % beachCount;
                sqls_temp[pageCursor] = sqls[i];
                if (pageCursor == (beachCount - 1)) {
                    rows = executeBeachSqls(sqls_temp, con);
                    for (int index = 0; index < rows.length; index++) {
                        if (rows[index] == -1) {
                            log.warn("row num -->" + (i - index)
                                    + " process failed.");
                        }
                    }
                    sqls_temp = null;
                    sqls_temp = new String[beachCount];
                    log.info("process sql  " + beachCount + " over,cursor -->"
                            + i);
                }
            }
            rows = executeBeachSqls(sqls_temp, con);
            for (int index = 0; index < rows.length; index++) {
                if (rows[index] == -1) {
                    log.warn("row num -->" + (sqls.length - index)
                            + " process failed.");
                }
            }
            log.info("Sql process All over!");
            return;
        } catch (SQLException ex) {
            log.error("BeachInsert data  failed. ", ex);
            return;
        } finally {
            try {
                DbUtils.close(con);
            } catch (Exception ex) {

            }
            System.gc();
        }
    }

    /**
     * <pre>
     * 使用批量策略向数据库执行sql语句。
     * sqls参数是sql语句形成的String数组。
     * 返回结果的int数组，对应着每条记录的执行结果。
     *
     * 注意：传入的参数有多少条记录，多少条记录就会成为一批数据进行一次执行。
     *      部分数据库缓冲较小，数量单位请自行控制！
     * </pre>
     *
     * @param sqls String[]
     * @return int[]
     */
    public int[] executeBeachSql(String[] sqls) {

        if (sqls == null || sqls.length == 0) {
            log.warn("No any data for insert!");
            return null;
        }
        Connection con = null;
        try {
            con = run.getDataSource().getConnection();
            return executeBeachSqls(sqls, con);
        } catch (Exception ex) {
            log.error("BeachInsert data  failed. ", ex);
            return null;
        } finally {
            try {
                DbUtils.close(con);
            } catch (Exception ex) {

            }
        }
    }

    /**
     * @param sqls
     * @param con  Connection
     * @return int[]
     */
    private int[] executeBeachSqls(String[] sqls, Connection con) {
        Statement stmt = null;

        try {

            stmt = con.createStatement();
            stmt.clearBatch();
            for (String sql : sqls) {
                stmt.addBatch(sql);
            }

            int[] res = stmt.executeBatch();
            for (int index = 0; index < res.length; index++) {
                if (res[index] == -1) {
                    log.warn("row num -->" + (sqls.length - index)
                            + " process failed.");
                }
            }
            log.info("Sql process rows count -->" + sqls.length);
            return res;
        } catch (SQLException ex) {
            log.error("BeachInsert data  failed. ", ex);
            return null;
        } finally {
            try {
                DbUtils.close(stmt);
            } catch (Exception ex) {

            }
        }
    }

    /**
     * <pre>
     * 向与对象类名同名的表中，批量插入记录，信息由objects数组对象提供。
     * 数据库中必须存在与对象类名同名的表
     * 另外，object对象参数类型符合标准JavaBean规范的字段才会被存储！
     * 也即属性字段如果没有对应的public的标准get、set方法，对应属性就无法被存储！
     * </pre>
     *
     * @param objects Object
     * @return int[]
     */
    public int[] executeBeachInsert(Object[] objects) {
        if (objects == null || objects.length == 0) {
            log.info("No any data for insert!");
            return null;
        }
        String tableName = objects[0].getClass().getSimpleName();
        return executeBeachInsert(tableName, objects);
    }

    /**
     * <pre>
     * 执行批量插入，传入表名，和要插入的记录。
     * 参数Object[]是需要插入的对象数组，每个对象对应一条记录。
     * 对象属性名称必须与表内字段名称一致
     * </pre>
     *
     * @param tableName String
     * @param objects   Object[]
     * @return int[]
     */
    public int[] executeBeachInsert(String tableName, Object[] objects) {
        if (objects == null || objects.length == 0) {
            log.info("No any data for insert!");
            return null;
        }
        String sql = getBeachInsertSql(tableName, objects[0]);
        Object[][] param = new Object[objects.length][];
        for (int i = 0; i < objects.length; i++) {
            param[i] = getParam(objects[i]);
        }
        return executeBeachSql(sql, param);
    }

    /**
     * <pre>
     * 执行批量插入，传入表名，和要插入的记录。
     * List参数中方的是Map对象，每个Map对象代表一条记录。
     * </pre>
     *
     * @param tableName String
     * @param data      List<Map>
     * @return int[]
     */
    @SuppressWarnings("unchecked")
    public int[] executeBeachInsert(String tableName, List<Map> data) {
        if (data == null || data.isEmpty()) {
            log.info("No any data for insert!");
            return null;
        }
        String sql = getBeachInsertSql(tableName, data.get(0));
        Object[][] param = getBeachInsertParam(data);
        return executeBeachSql(sql, param);
    }

    /**
     * <pre>
     * 执行批量插入，传入表名，和要插入的记录。
     * Vector参数中方的是Map对象，每个Map对象代表一条记录。
     * </pre>
     *
     * @param tableName String
     * @param data      Object[][]
     * @return int[]
     */
    @SuppressWarnings("unchecked")
    public int[] executeBeachInsert(String tableName, Vector<Map> data) {
        if (data == null || data.isEmpty()) {
            log.info("No any data for insert!");
            return null;
        }
        String sql = getBeachInsertSql(tableName, (Map) data.get(0));
        Object[][] param = getBeachInsertParam(data);
        return executeBeachSql(sql, param);
    }

    /**
     * 反射机制，获取对象的属性值，并且构成其他方法执行所必须的数组参数
     *
     * @param obj
     * @return Object[]
     */
    @SuppressWarnings("unchecked")
    private Object[] getParam(Object obj) {
        Class clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        ArrayList param = new ArrayList();
        for (Field field : fields) {
            if (!hasGetSetMethod(field, clazz)) {
                continue;
            }
            field.setAccessible(true);
            try {
                param.add(field.get(obj));
            } catch (IllegalArgumentException e) {
                param.add("");
                log.error("", e);
            } catch (IllegalAccessException e) {
                param.add("");
                log.error("", e);
            }
        }
        return param.toArray();
    }

    @SuppressWarnings("unchecked")
    private Object[][] getBeachInsertParam(List<Map> data) {
        Object[][] res = new Object[data.size()][data.get(0).size()];
        for (int i = 0; i < data.size(); i++) {
            Map mp = (Map) data.get(i);
            res[i] = mp.values().toArray();
        }
        return res;
    }

    @SuppressWarnings("unchecked")
    private String getBeachInsertSql(String tableName, Object obj) {
        StringBuffer sql = new StringBuffer("insert into ").append(tableName)
                .append(" (");
        StringBuffer fieldstr = new StringBuffer();
        StringBuffer values = new StringBuffer();
        Class clazz = obj.getClass();

        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (!hasGetSetMethod(field, clazz)) {
                continue;
            }
            field.setAccessible(true);
            fieldstr.append(field.getName()).append(",");
            values.append('?').append(",");
        }
        sql.append(substringBeforeLast(fieldstr.toString(), ",")).append(
                ") values (").append(
                substringBeforeLast(values.toString(), ",")).append(")");
        return sql.toString();
    }

    // 判断是不是标准bean属性！也就是必须有get、set属性的字段才储存
    @SuppressWarnings("unchecked")
    private boolean hasGetSetMethod(Field field, Class clazz) {
        String fieldName = field.getName();
        fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName
                .substring(0, 1).toUpperCase());
        String methoName = "get" + fieldName;
        try {
            clazz.getMethod(methoName);
        } catch (SecurityException e) {
            return false;// 任何错误，都说明不符合标准bean，就要返回假！
        } catch (NoSuchMethodException e) {
            return false;
        }
        return true;
    }

    @SuppressWarnings("unchecked")
    private String getBeachInsertSql(String tableName, Map data) {
        StringBuffer sql = new StringBuffer("insert into ").append(tableName)
                .append(" (");
        StringBuffer fields = new StringBuffer();
        StringBuffer values = new StringBuffer();
        for (Iterator iter = data.entrySet().iterator(); iter.hasNext(); ) {
            Map.Entry item = (Map.Entry) iter.next();
            fields.append(item.getKey()).append(",");
            values.append('?').append(",");
        }
        sql.append(substringBeforeLast(fields.toString(), ",")).append(
                ") values (").append(
                substringBeforeLast(values.toString(), ",")).append(")");
        return sql.toString();
    }

    private String substringBeforeLast(String resStr, String lastString) {
        log.debug("resStr-->" + resStr);
        String res = resStr.substring(0, resStr.lastIndexOf(lastString));
        return res;
    }

    private void showSql(String sql) {
        log.debug(sql);
    }

}
